#ifndef _SOCKET_STREAM_H_
#define _SOCKET_STREAM_H_

#ifdef	FD_SETSIZE
#undef	FD_SETSIZE
#define	FD_SETSIZE	256
#else
#define	FD_SETSIZE	256
#endif

#ifdef WIN32
#include <winsock2.h>
#pragma comment (lib, "ws2_32.lib")
#include <Iphlpapi.h>
#include "stdint.h"
#endif

#include "Feature.h"

#ifdef __APPLE__
#include <netinet/in.h>
#include <netinet/tcp.h>
#include "typedef_mac.h"
#endif

#ifdef __linux__
#include <netinet/in.h>
#include <netinet/tcp.h>
#include "typedef_linux.h"
#endif

#include "Mutex.h"

const int MAXCONNECTIONS = 5;	// Linux limit to 128, BSD limit to 5

#define PACKET_TYPE_ACK             (1 << MSG_TYPE_ACK)
#define PACKET_TYPE_PROBE           (1 << MSG_TYPE_PROBE)
#define PACKET_TYPE_AUTH            (1 << MSG_TYPE_AUTH)
#define PACKET_TYPE_HDR             (1 << 9)

#define	NET_MGR_ACCEPT_TIMEOUT		15			// accept timeout is 15 sec
#define	UDP_REPEAT_TIME				3
#define CTRLMGR_UDP_BROADCAST		0x1
#define CTRLMGR_UDP_UNICAST			(0x1 << 1)

enum {
    ERR_SOCKET_RECV_FAILED,
    ERR_SOCKET_SEND_FAILED,
};

enum SOCKET_TYPE
{
    SOCKET_TYPE_TCP = 1,
    SOCKET_TYPE_UDP,
    SOCKET_TYPE_SSL,
};

#ifdef __APPLE__
typedef struct {
    unsigned char PhysicalAddress[6];
    int PhysicalAddressLength;
    int PhysicalType;
} IP_ADAPTER_ADDRESSES, *PIP_ADAPTER_ADDRESSES;
#endif

class STCryptor;
class StZetaStamp;
class socket_stream
{
public:
    socket_stream(SOCKET = INVALID_SOCKET);
    virtual ~socket_stream(void);

public:
    bool			create_client_socket();
    virtual bool	create_server_socket(int port);

    bool			close(void);

    SOCKET			accept(int timeout);
    bool			connect(const char * host, const USHORT port);
    virtual int		send(const void *, unsigned int);
    virtual int		recv(void *, unsigned int);
    virtual int		Read(void *buf, int size, int streamtype){return -1;};
    virtual int 	Write(void *buf,int size, int streamtype){return -1;};

    void			set_block(const bool);
    void			set_timeout(DWORD rwtimeout);
    void			set_connect_block(const bool block);
    bool			is_valid(void) const;
    int				get_sockfd(void);

    virtual void 	DestorySSL(){};
    virtual SOCKET	SetupRelay(const char *szIP, int nPort, const char *szKey, BOOL bSSL, BOOL bProxy = FALSE, const char *szProxyIP = NULL, int nProxyport = 0, const unsigned char *szProxyUser = NULL, const unsigned char *szProxyPWD = NULL){return INVALID_SOCKET;};

    static int		GetAdapterIpBySourceSocketAddr(char *ip, sockaddr_in * src_addr);
    static void		DumpSocketError(const char *log, int err);

#ifdef WIN32 
    static void		IpAdapterFree(PIP_ADAPTER_ADDRESSES dup);
    static PIP_ADAPTER_ADDRESSES IpAdapterDup(PIP_ADAPTER_ADDRESSES adapter);
    static int		GetAdapterAddressByIp(char *ip, PIP_ADAPTER_ADDRESSES *adapter);
    static int		GetNetDevTypeByAdapterAddress(PIP_ADAPTER_ADDRESSES adapter);
    virtual void    SetSRS2RelayHeartBeatTimeout(unsigned int nTimeout){};
#elif defined __APPLE__
    void			IpAdapterFree(PIP_ADAPTER_ADDRESSES dup);
    void			GetAdapterAddressByIp(char *ip, PIP_ADAPTER_ADDRESSES adapter);
    int				GetNetDevTypeByAdapterAddress(PIP_ADAPTER_ADDRESSES adapter);
#endif

protected:
    int				send_packet(const void *, unsigned int, unsigned int smallpkt=0);
    int				recv_packet(void *, unsigned int);
    SOCKET			m_sockfd;
    SOCKET_TYPE		m_nSocktype;
    CBaseMutex		m_mutex;
    sockaddr_in		m_addr;
    STCryptor      *m_pCryptor;
    DWORD			m_nChannels;
    DWORD           m_bEncrypt;
    StZetaStamp		*m_ZetaSockPktSend;
    int				m_nBroadcast;

#if (ENABLE_CRYPTO)
    char			m_szSocketName[128];
public:
    DWORD			MonitorStreamRead(VOID* buf, DWORD size, DWORD channel);
    DWORD			MonitorStreamWrite(VOID* buf, DWORD size, DWORD channel, int offset=0);

    void			set_socket_name(const char* name);
    //    bool			set_encrypt_key(char* pKey, int len);
    //	void			set_encrypt(bool enable, DWORD mode);
    bool			is_crypted();

    void            set_stamp(StZetaStamp * stamp);
    StZetaStamp*	get_stamp();

    void			SetCryptor(STCryptor* pCryptor);
    void			SetEncryptChannels( DWORD nChannels ) { m_nChannels = nChannels; };
    DWORD			GetEncryptChannels() { return m_nChannels; };

    STCryptor*		get_cryptor() { return m_pCryptor; };

#if(ENABLE_PERF_LOG)
    int				log_send(const void *, unsigned int, bool bSmallPkt, bool bCrypted, int iOffset);
    int				log_recv(void *, unsigned int, bool bCrypted);
#endif

    int				send(const void *, unsigned int, bool bSmallPkt, bool bCrypted, int iOffset);
    int				recv(void *, unsigned int, bool bCrypted);
#endif

    friend class CControlManager;
};

#endif /* #ifndef _SOCKET_STREAM_H_ */
